package cn.ecommerce.course.module.users.service.users;

import cn.ecommerce.course.framework.common.pojo.CommonResult;
import cn.ecommerce.course.module.users.config.JwtConfig;
import cn.ecommerce.course.module.users.controller.app.users.dto.LoginDTO;
import cn.ecommerce.course.module.users.controller.app.users.dto.LoginResponseDTO;
import cn.ecommerce.course.module.users.controller.app.users.vo.SecurityUserVO;
import cn.ecommerce.course.module.users.controller.app.users.vo.UserRespInfoVO;
import cn.ecommerce.course.module.users.utils.JwtUtils;
import cn.ecommerce.course.module.users.utils.PhoneValidationUtils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

import cn.ecommerce.course.module.users.controller.admin.users.vo.*;
import cn.ecommerce.course.module.users.dal.dataobject.users.UsersDO;
import cn.ecommerce.course.framework.common.pojo.PageResult;
import cn.ecommerce.course.framework.common.pojo.PageParam;
import cn.ecommerce.course.framework.common.util.object.BeanUtils;

import cn.ecommerce.course.module.users.dal.mysql.users.UsersMapper;

import static cn.ecommerce.course.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.ecommerce.course.framework.common.util.collection.CollectionUtils.convertList;
import static cn.ecommerce.course.framework.common.util.collection.CollectionUtils.diffList;
import static cn.ecommerce.course.module.users.enums.ErrorCodeConstants.*;
import static cn.ecommerce.course.module.users.utils.RedisConstants.*;

/**
 * 电商平台用户 Service 实现类
 *
 * @author 恩
 */
@Service
@Validated
public class UsersServiceImpl implements UsersService {

    @Resource
    private UsersMapper usersMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private JwtUtils jwtUtils;

    @Resource
    private JwtConfig jwtConfig;
    /**
     * 注入Spring Security的认证管理器
     * 核心作用：负责验证用户提交的用户名和密码的合法性
     * 内部会调用自定义的UserDetailsService加载用户信息并与输入密码比对
     */
    @Resource
    private AuthenticationManager authenticationManager;

    @Override
    public Long createUsers(UsersSaveReqVO createReqVO) {
        // 插入
        UsersDO users = BeanUtils.toBean(createReqVO, UsersDO.class);
        usersMapper.insert(users);

        // 返回
        return users.getId();
    }

    @Override
    public void updateUsers(UsersSaveReqVO updateReqVO) {
        // 校验存在
        validateUsersExists(updateReqVO.getId());
        // 更新
        UsersDO updateObj = BeanUtils.toBean(updateReqVO, UsersDO.class);
        usersMapper.updateById(updateObj);
    }

    @Override
    public void deleteUsers(Long id) {
        // 校验存在
        validateUsersExists(id);
        // 删除
        usersMapper.deleteById(id);
    }

    @Override
        public void deleteUsersListByIds(List<Long> ids) {
        // 删除
        usersMapper.deleteByIds(ids);
        }


    private void validateUsersExists(Long id) {
        if (usersMapper.selectById(id) == null) {
            throw exception(USERS_NOT_EXISTS);
        }
    }

    @Override
    public UsersDO getUsers(Long id) {
        return usersMapper.selectById(id);
    }

    @Override
    public PageResult<UsersDO> getUsersPage(UsersPageReqVO pageReqVO) {
        return usersMapper.selectPage(pageReqVO);
    }

    /**
     * 获取验证码
     * @param loginDTO
     * @return
     */
    @Override
    public CommonResult<Boolean> sendCode(LoginDTO loginDTO) {
        //1.校验手机号是否为空
        if (loginDTO.getPhone() == null){
            return CommonResult.error(PHONE_IS_NULL);
        }
        // 2.校验手机号格式
        if(!PhoneValidationUtils.isValidChinaMobile(loginDTO.getPhone())){
            return CommonResult.error(PHONE_IS_ERROR);
        }
        // 3.符合格式，生成验证码
        String code = RandomUtil.randomNumbers(6);
        // 4.保存验证码到redis中,有效时间为5分钟
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+loginDTO.getPhone(), code, LOGIN_CODE_TTL, TimeUnit.MINUTES);
        // 5.发送验证码
        System.out.println("【短信验证码】发送给手机号" + loginDTO.getPhone() + "，验证码:" + code);
        // 6.发送成功
        return CommonResult.success( true);
    }

    /**
     * 验证码登录
     * @param loginDTO 登录信息
     * @return 登录结果
     */
    @Override
    public CommonResult<LoginResponseDTO> verifyCodeLogin(LoginDTO loginDTO) {
        // 1.校验手机号是否正确
        if(!PhoneValidationUtils.isValidChinaMobile(loginDTO.getPhone())){
            return CommonResult.error(PHONE_IS_ERROR);
        }
        // 2获取redis中的验证码
        String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + loginDTO.getPhone());
        // 3.校验验证码是否正确
        if (code == null || !code.equals(loginDTO.getCode())){
            return CommonResult.error(CODE_IS_ERROR);
        }
        // 4.验证码正确,查询是否是新用户
        UsersDO user = usersMapper.selectOne("phone", loginDTO.getPhone());
        // 5.1 如果是新用户,创建用户
        if(user == null){
            // 5.1创建用户
            user = createUser(loginDTO.getPhone());
        }
        // 6.生成token和用户信息
        LoginResponseDTO loginResponseDTO = createTokenAndUserInfo(user);
        // 7.移除redis中的验证码
        stringRedisTemplate.delete(LOGIN_CODE_KEY + loginDTO.getPhone());
        // 8.返回结果
        return CommonResult.success(loginResponseDTO);
    }

    /**
     * 账号密码登录
     * @param loginDTO 登录信息
     * @return 登录结果
     */
    @Override
    public CommonResult<LoginResponseDTO> accountLogin(LoginDTO loginDTO) {
        // 1. 构建未认证的身份令牌
        // UsernamePasswordAuthenticationToken是Spring Security中用于存储用户名密码的令牌对象
        // 此时的令牌处于未认证状态（credentials为原始密码）
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginDTO.getAccount(), loginDTO.getPassword());
        // 2. 调用AuthenticationManager进行身份认证
        // 内部流程：
        // - 调用UserDetailsService.loadUserByUsername()获取数据库中的用户信息（包含加密后的密码）
        // - 使用PasswordEncoder比对输入密码与数据库密码的一致性
        // - 认证通过则返回已认证的Authentication对象，失败则抛出异常
        Authentication authentication = authenticationManager.authenticate(authenticationToken);

        // 3. 从认证结果中提取用户信息
        // getPrincipal()方法返回认证主体，这里是自定义的SecurityUserVO对象（包含用户完整信息）
        SecurityUserVO securityUserVO = (SecurityUserVO) authentication.getPrincipal();
        // 4.将SecurityUserVO对象中的用户信息转换成DO对象
        UsersDO userDO = securityUserVO.getUserDO();
        // 5.生成token和用户信息
        LoginResponseDTO loginResponseDTO = createTokenAndUserInfo(userDO);
        // 6.返回结果
        return CommonResult.success(loginResponseDTO);
    }

    /**
     * 退出登录
     * @param loginResponseDTO 登录token
     * @return 退出登录结果
     */
    @Override
    public CommonResult<Boolean> logout(LoginResponseDTO loginResponseDTO) {
        // 1.判断token是否为空
        if (loginResponseDTO == null){
            return CommonResult.error(TOKEN_IS_NULL);
        }
        // 2.移除redis中的token
        stringRedisTemplate.delete(LOGIN_USER_KEY + loginResponseDTO.getAccessToken());
        // 3.清除SecurityContextHolder中的用户信息
        SecurityContextHolder.clearContext();
        // 4.返回结果
        return CommonResult.success(true);
    }

    /**
     * 创建用户信息
     * @param phone 手机号
     * @return 创建的用户信息
     */
    private UsersDO createUser(String phone){
        // 1.创建DO对象
        UsersDO usersDO = new UsersDO();
        // 2.设置属性
        usersDO.setPhone(phone); // 手机号
        usersDO.setAccount("tsj" + RandomUtil.randomNumbers(8)); // 账号
        usersDO.setNickname("新用户"+ RandomUtil.randomString(10)); // 昵称
        usersDO.setStatus(1); // 账号状态
        usersDO.setLastLoginTime(LocalDateTime.now()); // 最近登录时间
        // 3. 插入数据库
        usersMapper.insert(usersDO);
        return usersDO;
    }

    /**
     * 用于创建token和用户信息
     * @param user 用户信息
     * @return 创建好的token和用户信息
     */
    private LoginResponseDTO createTokenAndUserInfo(UsersDO user){
        // 1.生成token
        String token = jwtUtils.generateToken(user.getId(), user.getAccount());
        // 1.1 将token存入redis中
        stringRedisTemplate.opsForValue().set(LOGIN_USER_KEY + token, token,jwtConfig.getExpireTime(), TimeUnit.MILLISECONDS);
        // 2.创建登录响应DTO对象
        LoginResponseDTO loginResponseDTO = new LoginResponseDTO();
        // 2.1 设置属性
        loginResponseDTO.setAccessToken(token); // token
        loginResponseDTO.setUserInfo(BeanUtils.toBean(user, UserRespInfoVO.class)); // 用户信息
        // 3.返回结果
        return loginResponseDTO;
    }

}